/*
 * Created by tangming991 on 2020/12/21.
 * 词法扫描程序
*/


#include "../Headers/scan.h"

char ch;
int index=0;
std::string token;

/*回退一个字符*/
void retract() {
    index--;
}

/*检索是否关键字*/
int reserve() {
    if (token=="int")return _SYN_INT;
    if (token=="main")return _SYN_MAIN;
    if (token=="if")return _SYN_IF;
    if (token=="while")return _SYN_WHILE;
    if (token=="for")return _SYN_FOR;
    if (token=="printf")return _SYN_PRINTF;
    if (token=="return")return _SYN_RETURN;
    return _SYN_ID;
}

/*是否是数字*/
bool isDigit() {
    return ch >= '0' && ch <= '9';
}

/*是否是字母*/
bool isLetter() {
    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}

/*获取字符*/
void getCh() {
    ch =sources[index++];
}

/*扫描词法*/
Word Scanner() {
    Word word;
    token = "";
    getCh();
    if (isLetter()) {
        while (isLetter() || isDigit()) {
            token += ch;
            getCh();
        }
        retract();
        word.syn = reserve();
        word.value = token;
        return word;
    } else if (isDigit()) {
        while (isDigit()) {
            token += ch;
            getCh();
        }
        retract();
        word.syn = _SYN_NUM;
        word.value = token;
        return word;
    } else
        switch (ch) {
            case '+':
                word.syn = _SYN_PLUS;
                word.value = "+";
                return word;
            case '-':
                word.syn = _SYN_MINUS;
                word.value = "-";
                return word;
            case '*':
                word.syn = _SYN_TIMES;
                word.value = "*";
                return word;
            case '/':
                getCh();
                if (ch == '/') {
                    while (true) {
                        getCh();
                        if (ch == '\n') {
                            line++;
                            return Scanner();
                        }
                    }
                } else if (ch == '*') {
                    while (true) {
                        getCh();
                        if (ch == '*') {
                            getCh();
                            if (ch == '/') {
                                return Scanner();
                            }
                            retract();
                        } else if (ch == '\n')
                            line++;
                    }
                }
                retract();
                word.syn = _SYN_DIVIDE;
                word.value = "/";
                return word;
            case '<':
                getCh();
                if (ch == '=') {
                    word.syn = _SYN_LE;
                    word.value = "<=";
                    return word;
                }
                retract();
                word.syn = _SYN_LT;
                word.value = "<";
                return word;
            case '>':
                getCh();
                if (ch == '=') {
                    word.syn = _SYN_ME;
                    word.value = ">=";
                    return word;
                }
                retract();
                word.syn = _SYN_LG;
                word.value = ">";
                return word;
            case '=':
                getCh();
                if (ch == '=') {
                    word.syn = _SYN_EQ;
                    word.value = "==";
                    return word;
                }
                retract();
                word.syn = _SYN_ASSIGN;
                word.value = "=";
                return word;
            case '!':
                getCh();
                if (ch == '=') {
                    word.syn = _SYN_NE;
                    word.value = "!=";
                    return word;
                }
                word.syn = _SYN_EXC;
                word.value = "!";
                return word;
            case ';':
                word.syn = _SYN_SEMICOLON;
                word.value = ";";
                return word;
            case ':':
                word.syn = _SYN_COLON;
                word.value = ":";
                return word;
            case ',':
                word.syn = _SYN_COMMA;
                word.value = ",";
                return word;
            case '(':
                word.syn=_SYN_LPAREN;
                word.value="(";
                return word;
            case ')':
                word.syn=_SYN_RPAREN;
                word.value=")";
                return word;
            case '[':
                word.syn=_SYN_LBRACKET;
                word.value="[";
            case ']':
                word.syn=_SYN_RBRACKET;
                word.value="]";
            case '{':
                word.syn=_SYN_LBRACES;
                word.value="{";
                return word;
            case '}':
                word.syn=_SYN_RBRACES;
                word.value="}";
                return word;
            case '&':
                getCh();
                if (ch=='&'){
                    word.syn=_SYN_AND;
                    word.value="&&";
                    return word;
                }
                retract();
                word.syn=_SYN_BAND;
                word.value="&";
            case '|':
                getCh();
                if (ch=='|'){
                    word.syn=_SYN_OR;
                    word.value="||";
                    return word;
                }
                retract();
                word.syn=_SYN_BOR;
                word.value="|";
            case '"':
                getCh();
                while (ch!='"'){
                    token+=ch;
                    getCh();
                }
                word.syn=_SYN_STR;
                word.value=token;
                return word;
            case '\n':
            case '\r':
                line++;
                return Scanner();
            case '\t':
            case ' ':
                return Scanner();
            case '\0':
            case EOF :
                word.syn=_SYN_END;
                word.value="end";
                return word;
            default:
                word.syn=_SYN_ERROR;
                word.value="error word";
                return word;
        }
}

